package com.fbs.wowo.manage.service.imp;

import com.fbs.wowo.exception.ErrorCode;
import com.fbs.wowo.exception.InputVerifyFailedException;
import com.fbs.wowo.exception.NoSuchDataException;
import com.fbs.wowo.finance.mapper.BillMapper;
import com.fbs.wowo.finance.util.BillGenerator;
import com.fbs.wowo.finance.vo.BillQuery;
import com.fbs.wowo.manage.entity.Account;
import com.fbs.wowo.manage.entity.UserLogin;
import com.fbs.wowo.manage.mapper.AccountMapper;
import com.fbs.wowo.manage.service.AccountService;
import com.fbs.wowo.manage.service.VerifyCodeService;
import com.fbs.wowo.manage.vo.NamePassVo;
import com.fbs.wowo.manage.vo.Wallet;
import com.fbs.wowo.util.common.Validator;
import org.apache.shiro.crypto.RandomNumberGenerator;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;

/**
 * 账户
 */
@Service("accountService")
@Transactional
public class AccountServiceImp implements AccountService {

    /**
     * 校验支付密码
     * @param vo vo
     */
    @Override
    public void verifyPass(NamePassVo vo) throws InputVerifyFailedException {
        Validator.verifyStr(vo.getUserId());
        Account accountDb = verifyDbAccount(vo.getUserId());
        if (!StringUtils.hasText(accountDb.getPassword())) throw new NoSuchDataException(ErrorCode.ERR_PASS_NULL);
        // 对比数据库加密后的密码
        String hashedPasswordBase64 = new Sha256Hash(vo.getPassword(), accountDb.getSalt(), 1024).toBase64();
        if (!hashedPasswordBase64.equals(accountDb.getPassword())) throw new InputVerifyFailedException("密码错误");

        // TODO 错误到一定次数限制
        // 更改盐，重新插入
        RandomNumberGenerator rng = new SecureRandomNumberGenerator();
        ByteSource salt = rng.nextBytes();
        // 将盐插入数据库
        Account account = new Account();
        account.setId(accountDb.getId());
        account.setSalt(salt.toBase64());
        account.setPassword(new Sha256Hash(vo.getPassword(), account.getSalt(), 1024).toBase64());
        updateAccount(account);
    }

    /**
     * 用户是否设置过支付密码
     * @param   userId 用户id
     * @return  1 设置过 0 未设置过
     */
    @Override
    public int hasSetPass(String userId) {
        Validator.verifyStr(userId);
        Account accountDb = verifyDbAccount(userId);
        if (hasStr(accountDb.getPassword())) return 1;
        return 0;
    }
    /**
     * 修改支付密码
     */
    @Override
    public void modifyPayPassword(NamePassVo vo) throws NoSuchDataException {
        Validator.verifyStr(vo.getUserId());
        Validator.verifyStr(vo.getPassword());
        Account accountDb = verifyDbAccount(vo.getUserId());

        // 判断账户密码和盐是否存在，若不存在说明是新账户，生成盐
        if (!StringUtils.hasText(accountDb.getSalt())) {
            // 生成盐
            RandomNumberGenerator rng = new SecureRandomNumberGenerator();
            ByteSource salt = rng.nextBytes();
            // 将盐插入数据库
            accountDb.setSalt(salt.toBase64());
        }
        if (!hasStr(accountDb.getPassword())) {
            // 生成密码
            String hashedPasswordBase64 = new Sha256Hash(vo.getPassword(), accountDb.getSalt(), 1024).toBase64();
            accountDb.setPassword(hashedPasswordBase64);
        } else {
            // 对比原密码
            String hashedPasswordBase64 = new Sha256Hash(vo.getOldPassword(), accountDb.getSalt(), 1024).toBase64();
            if (!hashedPasswordBase64.equals(accountDb.getPassword())) throw new InputVerifyFailedException("旧密码输入错误");
            String newPassEncode = new Sha256Hash(vo.getPassword(), accountDb.getSalt(), 1024).toBase64();
            accountDb.setPassword(newPassEncode);
        }
        updateAccount(accountDb);
    }

    /**
     * 忘记支付密码
     */
    @Override
    public void forgetPayPassword(NamePassVo vo) {
        Validator.verifyStr(vo.getUserId());
        Validator.verifyStr(vo.getTel());
        Validator.verifyStr(vo.getPassword());
        // 先判断账户存不存在
        Account accountDb = verifyDbAccount(vo.getUserId());
        // 手机验证码
        UserLogin userLogin = verifyCodeService.verifyCode(vo.getTel(), vo.getVerifyCode());
        if (userLogin == null) throw new NoSuchDataException();

        // 生成盐
        RandomNumberGenerator rng = new SecureRandomNumberGenerator();
        ByteSource salt = rng.nextBytes();
        // 将盐插入数据库
        accountDb.setSalt(salt.toBase64());
        // 生成密码
        String hashedPasswordBase64 = new Sha256Hash(vo.getPassword(), accountDb.getSalt(), 1024).toBase64();
        accountDb.setPassword(hashedPasswordBase64);
        updateAccount(accountDb);
    }

    @Override
    public Account findByUserId(String userId) {
        return findAccountByUserId(userId);
    }

    /**
     * 获取钱包内容
     * 更新入账记录
     *
     * @param userId
     */
    public static final int Salary = 10;
    @Override
    public Wallet wallet(String userId) {
        Validator.verifyStr(userId);
        Account account = accountMapper.findByUserId(userId);
        if (account == null) throw new NoSuchDataException(ErrorCode.ERR_ACCOUNT_NULL);
        // 更新未发放的到已发放的
        Date deadline = deadline();
        billMapper.updateWallet(userId, deadline, BillGenerator.StatusWait, BillGenerator.StatusSuccess);

        // 累计收入
        BillQuery incomeQuery = new BillQuery();
        incomeQuery.setUserId(userId);
        incomeQuery.setIsIncome(BillGenerator.Income_Income);
        BigDecimal income = billMapper.countSelective(incomeQuery);
        if (income == null) income = new BigDecimal(0);

        // 累计可用收入
        BillQuery incomeValidQuery = new BillQuery();
        incomeValidQuery.setUserId(userId);
        incomeValidQuery.setIsIncome(BillGenerator.Income_Income);
        Date now = new Date();
        // 如果这个月到了15号，那么这个月之前的费用都可以提现
        // 如果这个月没到15号，那么上个月15之前的费用可以提现
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.DAY_OF_MONTH, Salary);
        if (now.getTime() < cal.getTime().getTime())
            cal.add(Calendar.MONTH, -1);
        Date salaryDay = cal.getTime();
        incomeValidQuery.setEndTime(salaryDay);
        BigDecimal validIncome = billMapper.countSelective(incomeQuery);
        if (validIncome == null) validIncome = new BigDecimal(0);

        // 当前余额
        // 余额支出
        // 支出只计算余额支付的支出
        BillQuery expendQuery = new BillQuery();
        expendQuery.setUserId(userId);
        expendQuery.setStatus(BillGenerator.StatusSuccess);
        expendQuery.setIsIncome(BillGenerator.Income_Outcome);
        expendQuery.setTradeType(BillGenerator.PayOri);
        // 累计收入减去支出
        BigDecimal expend = billMapper.countSelective(expendQuery);
        if (expend == null) expend = new BigDecimal(0);
        BigDecimal balance = validIncome.subtract(expend);
        if (balance.compareTo(new BigDecimal(0)) < 0) throw new InputVerifyFailedException("余额异常"); // TODO 余额异常

        // 更新余额
        account.setBalance(balance);    // TODO lock balance要随之改变
        accountMapper.updateByPrimaryKeySelective(account);

        // 待发放余额
        BillQuery waitQuery = new BillQuery();
        waitQuery.setUserId(userId);
        waitQuery.setStatus(BillGenerator.StatusSuccess);
        waitQuery.setIsIncome(BillGenerator.Income_Income);
        waitQuery.setBeginTime(salaryDay);
        BigDecimal unissued = billMapper.countSelective(waitQuery);
        if (unissued == null) unissued = new BigDecimal(0);

        Wallet wallet = new Wallet();
        wallet.setBalance(balance);
        wallet.setIncome(income);
        wallet.setUnissued(unissued);
        wallet.setUserId(userId);

        return wallet;
    }

    /*****************************************************************************
     *
     *
     * 私有方法
     *
     *
     *****************************************************************************/

    private boolean hasStr(String str) {
        return StringUtils.hasText(str);
    }

    private Account verifyDbAccount(String userId) {
        // 先判断账户存不存在
        Account accountDb = findAccountByUserId(userId);
        if (accountDb == null) throw new NoSuchDataException();
        return accountDb;
    }

    private Date deadline() {
        Date now = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.DAY_OF_MONTH, 20);
        Date payDay = calendar.getTime();
        Date deadline = new Date();
        if (now.getTime() < payDay.getTime()) {
            // 如果没到20号就结算上上个月的
            // 从上月1号以前开始结算
            calendar.add(Calendar.MONTH, -1);
            calendar.set(Calendar.DAY_OF_MONTH, 1);
            deadline = calendar.getTime();
        } else {
            // 如果到20号就结算上个月之前的
            // 从本月1号以前开始结算
            calendar.set(Calendar.DAY_OF_MONTH, 1);
            deadline = calendar.getTime();
        }
        return deadline;
    }

    private void updateAccount(Account accountDb) {
        accountMapper.updateByPrimaryKeySelective(accountDb);
    }

    private Account findAccountByUserId(String userId) {
        return accountMapper.findByUserId(userId);
    }

    @Autowired
    private AccountMapper accountMapper;

    @Autowired
    private VerifyCodeService verifyCodeService;

    @Autowired
    private BillMapper billMapper;

}
